home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / imap-3.0 / non-ANSI / imapd / imapd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-14  |  37.5 KB  |  1,316 lines

  1. /*
  2.  * Program:    IMAP2bis server
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    5 November 1990
  13.  * Last Edited:    15 July 1993
  14.  *
  15.  * Copyright 1993 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36. /* Parameter files */
  37.  
  38. #include "mail.h"
  39. #include "osdep.h"
  40. #include <stdio.h>
  41. #include <ctype.h>
  42. #include <netdb.h>
  43. #include <errno.h>
  44. #include <signal.h>
  45. #include <pwd.h>
  46. #include <sys/file.h>
  47. #include <sys/stat.h>
  48. #include <sys/time.h>
  49. #include "misc.h"
  50.  
  51.  
  52. /* Autologout timer */
  53. #define TIMEOUT 60*30
  54.  
  55. /* Size of temporary buffers */
  56. #define TMPLEN 8192
  57.  
  58.  
  59. /* Server states */
  60.  
  61. #define LOGIN 0
  62. #define SELECT 1
  63. #define OPEN 2
  64. #define LOGOUT 3
  65.  
  66. /* Global storage */
  67.  
  68. char *version = "7.3(63)";    /* version number of this server */
  69. struct itimerval timer;        /* timeout state */
  70. int state = LOGIN;        /* server state */
  71. int mackludge = 0;        /* MacMS kludge */
  72. int anonymous = 0;        /* non-zero if anonymous */
  73. long idletime = 0;        /* time we became idle */
  74. MAILSTREAM *stream = NIL;    /* mailbox stream */
  75. MAILSTREAM *tstream = NIL;    /* temporary mailbox stream */
  76. long nmsgs = 0;            /* number of messages */
  77. long recent = 0;        /* number of recent messages */
  78. char *host = NIL;        /* local host name */
  79. char *user = NIL;        /* user name */
  80. char *pass = NIL;        /* password */
  81. char *home = NIL;        /* home directory */
  82. char *flags = NIL;        /* flag text */
  83. char cmdbuf[TMPLEN];        /* command buffer */
  84. char *tag;            /* tag portion of command */
  85. char *cmd;            /* command portion of command */
  86. char *arg;            /* pointer to current argument of command */
  87. char *lsterr = NIL;        /* last error message from c-client */
  88. char *response = NIL;        /* command response */
  89. char *litbuf = NIL;        /* buffer to hold literals */
  90.  
  91.  
  92. /* Response texts */
  93.  
  94. char *win = "%s OK %s completed\015\012";
  95. char *altwin = "%s OK %s\015\012";
  96. char *lose = "%s NO %s failed: %s\015\012";
  97. char *misarg = "%s BAD Missing required argument to %s\015\012";
  98. char *badfnd = "%s BAD FIND option unrecognized: %s\015\012";
  99. char *badarg = "%s BAD Argument given to %s when none expected\015\012";
  100. char *badseq = "%s BAD Bogus sequence in %s\015\012";
  101. char *badatt = "%s BAD Bogus attribute list in %s\015\012";
  102. char *badlit = "%s BAD Bogus literal count in %s\015\012";
  103. char *toobig = "* BAD Command line too long\015\012";
  104. char *nulcmd = "* BAD Null command\015\012";
  105. char *argrdy = "+ Ready for argument\015\012";
  106.  
  107. /* Drivers we use */
  108.  
  109. extern DRIVER bezerkdriver,tenexdriver,imapdriver,newsdriver,nntpdriver,
  110.   dummydriver;
  111.  
  112.  
  113. /* Function prototypes */
  114.  
  115. void main  ();
  116. void clkint  ();
  117. void kodint  ();
  118. char *snarf  ();
  119. void fetch  ();
  120. void fetch_body  ();
  121. void fetch_body_part  ();
  122. void fetch_envelope  ();
  123. void fetch_encoding  ();
  124. void changed_flags  ();
  125. void fetch_flags  ();
  126. void fetch_internaldate  ();
  127. void fetch_rfc822  ();
  128. void fetch_rfc822_header  ();
  129. void fetch_rfc822_size  ();
  130. void fetch_rfc822_text  ();
  131. void penv  ();
  132. void pbody  ();
  133. void pstring  ();
  134. void paddr  ();
  135. long cstring  ();
  136. long caddr  ();
  137.  
  138. extern char *crypt  ();
  139.  
  140. /* Main program */
  141.  
  142. void main (argc,argv)
  143.     int argc;
  144.     char *argv[];
  145. {
  146.   int i;
  147.   char *s,*t = "OK",*u,*v;
  148.   struct hostent *hst;
  149.   void (*f) () = NIL;
  150.   mail_link (&tenexdriver);    /* install the Tenex mail driver */
  151.   mail_link (&bezerkdriver);    /* install the Berkeley mail driver */
  152.   mail_link (&imapdriver);    /* install the IMAP driver */
  153.   mail_link (&newsdriver);    /* install the netnews driver */
  154.   mail_link (&nntpdriver);    /* install the NNTP driver */
  155.   mail_link (&dummydriver);    /* install the dummy driver */
  156.   gethostname (cmdbuf,TMPLEN-1);/* get local name */
  157.   host = cpystr ((hst = gethostbyname (cmdbuf)) ? hst->h_name : cmdbuf);
  158.   rfc822_date (cmdbuf);        /* get date/time now */
  159.   if (i = getuid ()) {        /* logged in? */
  160.     if (!(s = (char *) getlogin ())) s = (getpwuid (i))->pw_name;
  161.     if (i < 0) anonymous = T;    /* must be anonymous if pseudo-user */
  162.     user = cpystr (s);        /* set up user name */
  163.     pass = cpystr ("*");    /* and fake password */
  164.     state = SELECT;        /* enter select state */
  165.     t = "PREAUTH";        /* pre-authorized */
  166.   }
  167.   printf ("* %s %s IMAP2bis Service %s at %s\015\012",t,host,version,cmdbuf);
  168.   fflush (stdout);        /* dump output buffer */
  169.   signal (SIGALRM,clkint);    /* prepare for clock interrupt */
  170.   signal (SIGUSR2,kodint);    /* prepare for Kiss Of Death */
  171.                 /* initialize timeout interval */
  172.   timer.it_interval.tv_sec = TIMEOUT;
  173.   timer.it_interval.tv_usec = 0;
  174.   do {                /* command processing loop */
  175.     idletime = time (0);    /* get the idle time now */
  176.                 /* get a command under timeout */
  177.     timer.it_value.tv_sec = TIMEOUT; timer.it_value.tv_usec = 0;
  178.     setitimer (ITIMER_REAL,&timer,NIL);
  179.     if (!fgets (cmdbuf,TMPLEN-1,stdin)) _exit (1);
  180.                 /* make sure timeout disabled */
  181.     timer.it_value.tv_sec = timer.it_value.tv_usec = 0;
  182.     setitimer (ITIMER_REAL,&timer,NIL);
  183.     idletime = 0;        /* not idle any more */
  184.                 /* no more last error or literal */
  185.     if (lsterr) fs_give ((void **) &lsterr);
  186.     if (litbuf) fs_give ((void **) &litbuf);
  187.                 /* find end of line */
  188.     if (!strchr (cmdbuf,'\012')) fputs (toobig,stdout);
  189.     else if (!(tag = strtok (cmdbuf," \015\012"))) fputs (nulcmd,stdout);
  190.     else if (!(cmd = strtok (NIL," \015\012")))
  191.       printf ("%s BAD Missing command\015\012",tag);
  192.     else {            /* parse command */
  193.       response = win;        /* set default response */
  194.       ucase (cmd);        /* canonicalize command case */
  195.                 /* snarf argument */
  196.       arg = strtok (NIL,"\015\012");
  197.                 /* LOGOUT command always valid */
  198.       if (!strcmp (cmd,"LOGOUT")) {
  199.     if (state == OPEN) mail_close (stream);
  200.     stream = NIL;
  201.     printf("* BYE %s IMAP2bis server terminating connection\015\012",host);
  202.     state = LOGOUT;
  203.       }
  204.  
  205.                 /* kludge for MacMS */
  206.       else if (!strcmp (cmd,"VERSION")) {
  207.                 /* single argument */
  208.     if (!(s = snarf (&arg))) response = misarg;
  209.     else if (arg) response = badarg;
  210.     else {
  211.       if (!((i = atoi (s)) && i > 0 && i <= 4))
  212.         response = "%s BAD Unknown version\015\012";
  213.       else if (mackludge = (i == 4))
  214.         fputs ("* OK [MacMS] The MacMS kludge is enabled\015\012",stdout);
  215.     }
  216.       }
  217.       else if (!strcmp (cmd,"NOOP")) {
  218.     if ((state == OPEN) && !mail_ping (stream)) {
  219.       printf ("* BYE %s Fatal mailbox error: %s\015\012",host,
  220.           lsterr ? lsterr : "<unknown>");
  221.       state = LOGOUT;    /* go away */
  222.     }
  223.       }
  224.       else switch (state) {    /* dispatch depending upon state */
  225.       case LOGIN:        /* waiting to get logged in */
  226.     if (!strcmp (cmd,"LOGIN")) {
  227.       struct stat sbuf;
  228.       struct passwd *pwd;
  229.       fs_give ((void **) &user);
  230.       fs_give ((void **) &pass);
  231.                 /* two arguments */
  232.       if (!((user = cpystr (snarf (&arg))) &&
  233.         (pass = cpystr (snarf (&arg))))) response = misarg;
  234.       else if (arg) response = badarg;
  235.                 /* see if username and password are OK */
  236.       else if (server_login (user,pass,&home,argc,argv)) state = SELECT;
  237.                 /* nope, see if we allow anonymous */
  238.       else if (!stat ("/etc/anonymous.newsgroups",&sbuf) &&
  239.            !strcmp (user,"anonymous") && (pwd = getpwnam ("nobody"))) {
  240.         anonymous = T;    /* note we are anonymous, login as nobody */
  241.         setgid (pwd->pw_gid);
  242.         setuid (pwd->pw_uid);
  243.         state = SELECT;    /* make selected */
  244.       }
  245.       else response = "%s NO Bad %s user name and/or password\015\012";
  246.     }
  247.     else response = "%s BAD Command unrecognized/login please: %s\015\012";
  248.     break;
  249.  
  250.       case OPEN:        /* valid only when mailbox open */
  251.                 /* fetch mailbox attributes */
  252.     if (!strcmp (cmd,"FETCH")) {
  253.       if (!(arg && (s = strtok (arg," ")) && (t = strtok(NIL,"\015\012"))))
  254.         response = misarg;
  255.       else fetch (s,t);    /* do the fetch */
  256.     }
  257.                 /* fetch partial mailbox attributes */
  258.     else if (!strcmp (cmd,"PARTIAL")) {
  259.       unsigned long msgno,start,count,size;
  260.       if (!(arg && (msgno = strtol (arg,&s,10)) && (t = strtok (s," ")) &&
  261.         (s = strtok (NIL,"\015\012")) && (start = strtol (s,&s,10)) &&
  262.         (count = strtol (s,&s,10)))) response = misarg;
  263.       else if (s && *s) response = badarg;
  264.       else if (msgno > stream->nmsgs) response = badseq;
  265.       else {        /* looks good */
  266.         u = s = NIL;    /* no strings yet */
  267.         if (!strcmp (ucase (t),"RFC822")) {
  268.                 /* have to make a temporary buffer for this */
  269.           size = mail_elt (stream,msgno)->rfc822_size;
  270.           s = u = (char *) fs_get (size + 1);
  271.           strcpy (u,mail_fetchheader (stream,msgno));
  272.           strcat (u,mail_fetchtext (stream,msgno));
  273.           u[size] = '\0';    /* tie off string */
  274.         }
  275.         else if (!strcmp (t,"RFC822.HEADER"))
  276.           size = strlen (s = mail_fetchheader (stream,msgno));
  277.         else if (!strcmp (t,"RFC822.TEXT"))
  278.           size = strlen (s = mail_fetchtext (stream,msgno));
  279.         else if (*t == 'B' && t[1] == 'O' && t[2] == 'D' && t[3] == 'Y' &&
  280.              t[4] == '[' && *(t += 5)) {
  281.           if ((v = strchr (t,']')) && !v[1]) {
  282.         *v = '\0';    /* tie off body part */
  283.         s = mail_fetchbody (stream,msgno,t,&size);
  284.           }
  285.         }
  286.         if (s) {        /* got a string back? */
  287.           if (size <= --start) s = NIL;
  288.           else {        /* have string we can make smaller */
  289.         s += start;    /* this is the first byte */
  290.                 /* tie off as appropriate */
  291.         if (count < (size - start)) s[count] = '\0';
  292.           }
  293.           printf ("* %ld FETCH %s (",msgno,t);
  294.           pstring (s);    /* write the string */
  295.           fputs (")\015\012",stdout);
  296.           if (u) fs_give ((void **) &u);
  297.         }
  298.         else response = badatt;
  299.       }
  300.     }
  301.  
  302.                 /* store mailbox attributes */
  303.     else if (!strcmp (cmd,"STORE")) {
  304.                 /* must have three arguments */
  305.       if (!(arg && (s = strtok (arg," ")) && (cmd = strtok (NIL," ")) &&
  306.         (t = strtok (NIL,"\015\012")))) response = misarg;
  307.       else if (!strcmp (ucase (cmd),"+FLAGS")) f = mail_setflag;
  308.       else if (!strcmp (cmd,"-FLAGS")) f = mail_clearflag;
  309.       else if (!strcmp (cmd,"FLAGS")) {
  310.         mail_clearflag (stream,s,flags);
  311.         f = mail_setflag;    /* gross, but rarely if ever done */
  312.       }
  313.       else response = "%s BAD STORE %s not yet implemented\015\012";
  314.       if (f) {        /* if a function was selected */
  315.         (*f) (stream,s,t);    /* do it */
  316.         fetch (s,"FLAGS");    /* get the new flags status */
  317.       }
  318.     }
  319.                 /* check for new mail */
  320.     else if (!strcmp (cmd,"CHECK")) {
  321.                 /* no arguments */
  322.       if (arg) response = badarg;
  323.       else if (anonymous) mail_ping (stream);
  324.       else mail_check (stream);
  325.     }
  326.                 /* expunge deleted messages */
  327.     else if (!(anonymous || strcmp (cmd,"EXPUNGE"))) {
  328.                 /* no arguments */
  329.       if (arg) response = badarg;
  330.       else mail_expunge (stream);
  331.     }
  332.                 /* copy message(s) */
  333.     else if (!(anonymous || strcmp (cmd,"COPY"))) {
  334.       if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
  335.       else if (arg) response = badarg;
  336.       else mail_copy (stream,s,t);
  337.     }
  338.  
  339.                 /* search mailbox */
  340.     else if (!strcmp (cmd,"SEARCH")) {
  341.                 /* one or more arguments required */
  342.       if (!arg) response = misarg;
  343.       else {        /* zap search vector */
  344.                 /* end with a literal argument? */
  345.         while ((*(t = arg + strlen (arg) - 1) == '}') &&
  346.            (s = strrchr (arg,'{')) && response == win) {
  347.                 /* get length of literal, validate */
  348.           if (((i = atoi (++s)) < 1) || (TMPLEN - (t++ - cmdbuf) < i + 10))
  349.         response = badlit;
  350.           else {
  351.         *t++ = '\015';    /* reappend CR/LF */
  352.         *t++ = '\012';
  353.         fputs (argrdy,stdout);
  354.         fflush (stdout);/* dump output buffer */
  355.                 /* copy the literal */
  356.         while (i--) *t++ = getchar ();
  357.                 /* start a timeout */
  358.         timer.it_value.tv_sec = TIMEOUT; timer.it_value.tv_usec = 0;
  359.         setitimer (ITIMER_REAL,&timer,NIL);
  360.                 /* get new command tail */
  361.         if (!fgets (t,TMPLEN - (t-cmdbuf) - 1,stdin)) _exit (1);
  362.                 /* clear timeout */
  363.         timer.it_value.tv_sec = timer.it_value.tv_usec = 0;
  364.         setitimer (ITIMER_REAL,&timer,NIL);
  365.                 /* find end of line */
  366.         if (!(s = strchr (t,'\012'))) response = toobig;
  367.         else {
  368.           *s = NIL;    /* tie off line */
  369.           if (*--s == '\015') *s = NIL;
  370.         }
  371.           }
  372.         }
  373.                 /* punt if error */
  374.         if (response != win) break;
  375.                 /* do the search */
  376.         mail_search (stream,arg);
  377.         if (response == win || response == altwin) {
  378.                 /* output search results */
  379.           fputs ("* SEARCH",stdout);
  380.           for (i = 1; i <= nmsgs; ++i)
  381.         if (mail_elt (stream,i)->searched) printf (" %d",i);
  382.           fputs ("\015\012",stdout);
  383.         }
  384.       }
  385.     }
  386.     else            /* fall into select case */
  387.  
  388.       case SELECT:        /* valid whenever logged in */
  389.                 /* select new mailbox */
  390.     if ((!(anonymous || strcmp (cmd,"SELECT"))) ||
  391.         (!strcmp (cmd,"BBOARD")) || (!strcmp (cmd,"EXAMINE"))) {
  392.                 /* single argument */
  393.       if (!(s = snarf (&arg))) response = misarg;
  394.       else if (arg) response = badarg;
  395.       else {
  396.         char tmp[MAILTMPLEN];
  397.         sprintf (tmp,"%s%s",(*cmd == 'B') ? "*" : "",s);
  398.         recent = -1;    /* make sure we get an update */
  399.         if ((stream = mail_open (stream,tmp,anonymous ? OP_ANONYMOUS : NIL
  400.                      + (*cmd == 'E') ? OP_READONLY : NIL))
  401.         && ((response == win) || (response == altwin))) {
  402.                 /* flush old list */
  403.           fs_give ((void **) &flags);
  404.           s = tmp;        /* write flags here */
  405.           *s = '(';        /* start new flag list */
  406.           s[1] = '\0';
  407.           for (i = 0; i < NUSERFLAGS; i++)
  408.         if (t = stream->user_flags[i]) strcat (strcat (s,t)," ");
  409.                 /* append system flags to list */
  410.           strcat (s,"\\Answered \\Flagged \\Deleted \\Seen)");
  411.                 /* output list of flags */
  412.           printf ("* FLAGS %s\015\012",(flags = cpystr (s)));
  413.           state = OPEN;
  414.                 /* note readonly/readwrite */
  415.           response = stream->readonly ?
  416.         "%s OK [READ-ONLY] %s completed\015\012" :
  417.           "%s OK [READ-WRITE] %s completed\015\012";
  418.         }
  419.         else {        /* nuke if still open */
  420.           if (stream) mail_close (stream);
  421.           stream = NIL;
  422.           state = SELECT;    /* no mailbox open now */
  423.           response = lose;    /* open failed */
  424.         }
  425.       }
  426.     }
  427.                 /* APPEND message to mailbox */
  428.     else if (!(anonymous || strcmp (cmd,"APPEND"))) {
  429.       if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
  430.       else if (arg) response = badarg;
  431.       else {        /* append the data */
  432.         STRING st;
  433.         INIT (&st,mail_string,(void *) t,strlen (t));
  434.         mail_append (NIL,s,&st);
  435.       }
  436.     }
  437.  
  438.                 /* find mailboxes or bboards */
  439.     else if (!strcmp (cmd,"FIND")) {
  440.       response = "%s OK FIND %s completed\015\012";
  441.                 /* get subcommand and true argument */
  442.       if (!(arg && (s = strtok (arg," \015\012")) && (cmd = ucase (s)) &&
  443.         (arg = strtok (NIL,"\015\012")) && (s = snarf (&arg))))
  444.         response = misarg;    /* missing required argument */
  445.       else if (arg) response = badarg;
  446.       else if (anonymous) {    /* special version for anonymous users */
  447.         if (!strcmp (cmd,"BBOARDS")) mail_find_bboards (NIL,s);
  448.         else if (!strcmp (cmd,"ALL.BBOARDS")) mail_find_all_bboard (NIL,s);
  449.                 /* bboards not supported here */
  450.             else response = badfnd;
  451.       }
  452.       else {        /* dispatch based on type */
  453.         if ((!strcmp (cmd,"MAILBOXES"))||(!strcmp (cmd,"ALL.MAILBOXES"))) {
  454.           if (*s == '{') tstream = mail_open (NIL,s,OP_HALFOPEN);
  455.           if (*cmd == 'A') { /* want them all? */
  456.         mail_find_all (NIL,s);
  457.         if (tstream) mail_find_all (tstream,s);
  458.           }
  459.           else {        /* just subscribed */
  460.         mail_find (NIL,s);
  461.         if (tstream) mail_find (tstream,s);
  462.           }
  463.         }
  464.         else if ((!strcmp (cmd,"BBOARDS"))||(!strcmp (cmd,"ALL.BBOARDS"))){
  465.           char tmp[MAILTMPLEN];
  466.           if (*s == '{') {    /* prepend leading * if remote */
  467.         sprintf (tmp,"*%s",s);
  468.         tstream = mail_open (NIL,tmp,OP_HALFOPEN);
  469.           }
  470.           if (*cmd == 'A') { /* want them all? */
  471.         mail_find_all_bboard (NIL,s);
  472.         if (tstream) mail_find_all_bboard (tstream,s);
  473.           }
  474.           else {        /* just subscribed */
  475.         mail_find_bboards (NIL,s);
  476.         if (tstream) mail_find_bboards (tstream,s);
  477.           }
  478.         }
  479.         else response = badfnd;
  480.         if (tstream) mail_close (tstream);
  481.         tstream = NIL;    /* no more temporary stream */
  482.       }
  483.     }
  484.  
  485.                 /* subscribe to mailbox or bboard */
  486.     else if (!(anonymous || strcmp (cmd,"SUBSCRIBE"))) {
  487.       response = "%s OK SUBSCRIBE %s completed\015\012";
  488.                 /* get subcommand and true argument */
  489.       if (!(arg && (s = strtok (arg," \015\012")) && (cmd = ucase (s)) &&
  490.         (arg = strtok (NIL,"\015\012")) && (s = snarf (&arg))))
  491.         response = misarg;    /* missing required argument */
  492.       else if (arg) response = badarg;
  493.       else {        /* dispatch based on type */
  494.         if (!strcmp (cmd,"MAILBOX")) mail_subscribe (NIL,s);
  495.         else if (!strcmp (cmd,"BBOARD")) mail_subscribe_bboard (NIL,s);
  496.         else response = "%s BAD SUBSCRIBE option unrecognized: %s\015\012";
  497.       }
  498.     }
  499.                 /* unsubscribe to mailbox or bboard */
  500.     else if (!(anonymous || strcmp (cmd,"UNSUBSCRIBE"))) {
  501.       response = "%s OK UNSUBSCRIBE %s completed\015\012";
  502.                 /* get subcommand and true argument */
  503.       if (!(arg && (s = strtok (arg," \015\012")) && (cmd = ucase (s)) &&
  504.         (arg = strtok (NIL,"\015\012")) && (s = snarf (&arg))))
  505.         response = misarg;    /* missing required argument */
  506.       else if (arg) response = badarg;
  507.       else {        /* dispatch based on type */
  508.         if (!strcmp (cmd,"MAILBOX")) mail_unsubscribe (NIL,s);
  509.         else if (!strcmp (cmd,"BBOARD")) mail_unsubscribe_bboard (NIL,s);
  510.         else response="%s BAD UNSUBSCRIBE option unrecognized: %s\015\012";
  511.       }
  512.     }
  513.                 /* create mailbox */
  514.     else if (!(anonymous || strcmp (cmd,"CREATE"))) {
  515.       if (!(s = snarf (&arg))) response = misarg;
  516.       else if (arg) response = badarg;
  517.       else if (state == OPEN) mail_create (stream,s);
  518.       else {        /* use the same driver as INBOX */
  519.         if (tstream = mail_open (NIL,"INBOX",OP_PROTOTYPE)) {
  520.           mail_create (tstream,s);
  521.           tstream = NIL;    /* drop prototype */
  522.         }
  523.         else response = "%s NO Can't %s without an INBOX";
  524.       }
  525.     }
  526.                 /* delete mailbox */
  527.     else if (!(anonymous || strcmp (cmd,"DELETE"))) {
  528.       if (!(s = snarf (&arg))) response = misarg;
  529.       else if (arg) response = badarg;
  530.       else mail_delete (NIL,s);
  531.     }
  532.                 /* rename mailbox */
  533.     else if (!(anonymous || strcmp (cmd,"RENAME"))) {
  534.       if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
  535.       else if (arg) response = badarg;
  536.       else mail_rename (NIL,s,t);
  537.     }
  538.  
  539.                 /* purge cache */
  540.     else if (!strcmp (cmd,"PURGE")) {
  541.       response = "%s OK PURGE %s is unnecessary with this server\015\012";
  542.                 /* get subcommand */
  543.       if (!(arg && (s = strtok (arg," \015\012")) && (cmd = ucase (s))))
  544.         response = misarg;    /* missing subcommand */
  545.       arg = strtok (NIL,"\015\012");
  546.       if (strcmp (cmd,"ALWAYS")) {
  547.         if (!(arg && (s = snarf (&arg)))) response = misarg;
  548.         else if (arg) response = badarg;
  549.         if (strcmp (cmd,"STATUS") && strcmp (cmd,"STRUCTURE") &&
  550.         strcmp (cmd,"TEXTS"))
  551.           response = "%s NO PURGE %s is unknown to this server\015\012";
  552.       }
  553.       else if (arg) response = badarg;
  554.     }
  555.  
  556.     else response = "%s BAD Command unrecognized: %s\015\012";
  557.     break;
  558.       default:
  559.         response = "%s BAD Server in unknown state for %s command\015\012";
  560.     break;
  561.       }
  562.                 /* change in recent messages? */
  563.       if ((state == OPEN) && (recent != stream->recent))
  564.     printf ("* %d RECENT\015\012",(recent = stream->recent));
  565.                 /* get text for alternative win message now */
  566.       if (response == altwin) cmd = lsterr;
  567.                 /* output response */
  568.       printf (response,tag,cmd,lsterr);
  569.     }
  570.     fflush (stdout);        /* make sure output blatted */
  571.   } while (state != LOGOUT);    /* until logged out */
  572.   exit (0);            /* all done */
  573. }
  574.  
  575.  
  576. /* Clock interrupt
  577.  */
  578.  
  579. void clkint ()
  580. {
  581.   fputs ("* BYE Autologout; idle for too long\015\012",stdout);
  582.   fflush (stdout);        /* make sure output blatted */
  583.                 /* try to gracefully close the stream */
  584.   if (state == OPEN) mail_close (stream);
  585.   stream = NIL;
  586.   exit (0);            /* die die die */
  587. }
  588.  
  589.  
  590. /* Kiss Of Death interrupt
  591.  */
  592.  
  593. void kodint ()
  594. {
  595.   long t = time (0);
  596.   if (state == OPEN) {        /* must be open for this to work */
  597.     fputs ("* OK [READ-ONLY] Now READ-ONLY.  Mailbox lock surrendered\015\012",
  598.        stdout);
  599.     fflush (stdout);        /* make sure output blatted */
  600.     stream->readonly = T;    /* make the stream readonly */
  601.     mail_ping (stream);        /* cause it to stick! */
  602.   }
  603. }
  604.  
  605. /* Snarf an argument
  606.  * Accepts: pointer to argument text pointer
  607.  * Returns: argument
  608.  */
  609.  
  610. char *snarf (arg)
  611.     char **arg;
  612. {
  613.   long i;
  614.   char *c = *arg;
  615.   char *s = c + 1;
  616.   char *t = NIL;
  617.   if (!c) return NIL;        /* better be an argument */
  618.   switch (*c) {            /* see what the argument is */
  619.   case '\0':            /* catch bogons */
  620.   case ' ':
  621.     return NIL;
  622.   case '"':            /* quoted string */
  623.     if (!(strchr (s,'"') && (c = strtok (c,"\"")))) return NIL;
  624.     break;
  625.   case '{':            /* literal string */
  626.     if (isdigit (*s)) {        /* be sure about that */
  627.       i = strtol (s,&t,10);    /* get its length */
  628.                 /* validate end of literal */
  629.       if (*t++ != '}' || *t++) return NIL;
  630.       fputs (argrdy,stdout);    /* tell client ready for argument */
  631.       fflush (stdout);        /* dump output buffer */
  632.                 /* get a literal buffer */
  633.       c = litbuf = (char *) fs_get (i+1);
  634.                 /* start timeout */
  635.       timer.it_value.tv_sec = TIMEOUT; timer.it_value.tv_usec = 0;
  636.       setitimer (ITIMER_REAL,&timer,NIL);
  637.       while (i--) *c++ = getchar ();
  638.       *c++ = NIL;        /* make sure string tied off */
  639.       c = litbuf;        /* return value */
  640.                     /* get new command tail */
  641.       if (!fgets ((*arg = t),TMPLEN - (t - cmdbuf) - 1,stdin)) _exit (1);
  642.                 /* clear timeout */
  643.       timer.it_value.tv_sec = timer.it_value.tv_usec = 0;
  644.       setitimer (ITIMER_REAL,&timer,NIL);
  645.       if (!strchr (t,'\012')) {    /* have a newline there? */
  646.     response = toobig;    /* lost it seems */
  647.     return NIL;
  648.       }
  649.       break;
  650.     }
  651.                 /* otherwise fall through (third party IMAP) */
  652.   default:            /* atomic string */
  653.     c = strtok (c," \015\012");
  654.     break;
  655.   }
  656.                 /* remainder of arguments */
  657.   if ((*arg = strtok (t,"\015\012")) && **arg == ' ') ++*arg;
  658.   return c;
  659. }
  660.  
  661. /* Fetch message data
  662.  * Accepts: sequence as a string
  663.  *        string of data items to be fetched
  664.  */
  665.  
  666. #define MAXFETCH 100
  667.  
  668. void fetch (s,t)
  669.     char *s;
  670.     char *t;
  671. {
  672.   char c,*v;
  673.   long i,k;
  674.   BODY *b;
  675.   int parse_bodies = NIL;
  676.   void (*f[MAXFETCH]) ();
  677.   char *fa[MAXFETCH];
  678.   if (!mail_sequence (stream,s)) {
  679.     response = badseq;        /* punt if sequence bogus */
  680.     return;
  681.   }
  682.                 /* process macros */
  683.   if (!strcmp (ucase (t),"ALL"))
  684.     strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)");
  685.   else if (!strcmp (t,"FULL"))
  686.     strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)");
  687.   else if (!strcmp (t,"FAST")) strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE)");
  688.   s = t + strlen (t) - 1;    /* last character in attribute string */
  689.                 /* if multiple items, make sure in list form */
  690.   if (strchr (t,' ') && ((*t != '(') || (*s != ')'))) {
  691.     response = badatt;
  692.     return;
  693.   }
  694.                 /* nuke the parens now */
  695.   if ((*t == '(') && (*s == ')') && t++) *s = '\0';
  696.   k = 0;            /* initial index */
  697.   if (s = strtok (t," ")) do {    /* parse attribute list */
  698.     if (*s == 'B' && s[1] == 'O' && s[2] == 'D' && s[3] == 'Y') {
  699.       parse_bodies = T;        /* we will need to parse bodies */
  700.       switch (s[4]) {
  701.       case '\0':        /* entire body */
  702.     f[k++] = fetch_body;
  703.     break;
  704.       case '[':            /* body segment */
  705.     if ((v = strchr (s + 5,']')) && (!(*v = v[1]))) {
  706.       fa[k] = s + 5;    /* set argument */
  707.       f[k++] = fetch_body_part;
  708.       break;        /* valid body segment */
  709.     }            /* else fall into default case */
  710.       default:            /* bogus */
  711.     response = badatt;
  712.     return;
  713.       }
  714.     }
  715.     else if (!strcmp (s,"ENVELOPE")) f[k++] = fetch_envelope;
  716.     else if (!strcmp (s,"FLAGS")) f[k++] = fetch_flags;
  717.     else if (!strcmp (s,"INTERNALDATE")) f[k++] = fetch_internaldate;
  718.     else if (!strcmp (s,"RFC822")) f[k++] = fetch_rfc822;
  719.     else if (!strcmp (s,"RFC822.HEADER")) f[k++] = fetch_rfc822_header;
  720.     else if (!strcmp (s,"RFC822.SIZE")) f[k++] = fetch_rfc822_size;
  721.     else if (!strcmp (s,"RFC822.TEXT")) f[k++] = fetch_rfc822_text;
  722.     else {            /* unknown attribute */
  723.       response = badatt;
  724.       return;
  725.     }
  726.   } while ((s = strtok (NIL," ")) && k < MAXFETCH);
  727.   else {
  728.     response = misarg;        /* missing attribute list */
  729.     return;
  730.   }
  731.   if (s) {            /* too many attributes? */
  732.     response = "%s BAD Excessively complex FETCH attribute list\015\012";
  733.     return;
  734.   }
  735.   f[k++] = NIL;            /* tie off attribute list */
  736.                 /* for each requested message */
  737.   for (i = 1; i <= nmsgs; i++) if (mail_elt (stream,i)->sequence) {
  738.                 /* parse envelope, set body, do warnings */
  739.     mail_fetchstructure (stream,i,parse_bodies ? &b : NIL);
  740.     printf ("* %d FETCH (",i);    /* leader */
  741.     (*f[0]) (i,fa[0]);        /* do first attribute */
  742.     for (k = 1; f[k]; k++) {    /* for each subsequent attribute */
  743.       putchar (' ');        /* delimit with space */
  744.       (*f[k]) (i,fa[k]);    /* do that attribute */
  745.     }
  746.     fputs (")\015\012",stdout);    /* trailer */
  747.   }
  748. }
  749.  
  750. /* Fetch message body structure
  751.  * Accepts: message number
  752.  *        string argument
  753.  */
  754.  
  755.  
  756. void fetch_body (i,s)
  757.     long i;
  758.     char *s;
  759. {
  760.   BODY *body;
  761.   mail_fetchstructure (stream,i,&body);
  762.   fputs ("BODY ",stdout);    /* output attribute */
  763.   pbody (body);            /* output body */
  764. }
  765.  
  766.  
  767. /* Fetch message body part
  768.  * Accepts: message number
  769.  *        string argument
  770.  */
  771.  
  772. void fetch_body_part (i,s)
  773.     long i;
  774.     char *s;
  775. {
  776.   unsigned long j;
  777.   long k = 0;
  778.   BODY *body;
  779.   int f = mail_elt (stream,i)->seen;
  780.   mail_fetchstructure (stream,i,&body);
  781.   printf ("BODY[%s] ",s);    /* output attribute */
  782.   if (body && (s = mail_fetchbody (stream,i,s,&j))) {
  783.                 /* and literal string */
  784.     printf ("{%d}\015\012",j);
  785.     while (j -= k) k = fwrite (s += k,1,j,stdout);
  786.     changed_flags (i,f);    /* output changed flags */
  787.   }
  788.   else fputs ("NIL",stdout);    /* can't output anything at all */
  789. }
  790.  
  791. /* Fetch IMAP envelope
  792.  * Accepts: message number
  793.  *        string argument
  794.  */
  795.  
  796. void fetch_envelope (i,s)
  797.     long i;
  798.     char *s;
  799. {
  800.   ENVELOPE *env = mail_fetchstructure (stream,i,NIL);
  801.   fputs ("ENVELOPE ",stdout);    /* output attribute */
  802.   penv (env);            /* output envelope */
  803. }
  804.  
  805. /* Fetch flags
  806.  * Accepts: message number
  807.  *        string argument
  808.  */
  809.  
  810. void fetch_flags (i,s)
  811.     long i;
  812.     char *s;
  813. {
  814.   char tmp[MAILTMPLEN];
  815.   unsigned long u;
  816.   char *t;
  817.   MESSAGECACHE *elt = mail_elt (stream,i);
  818.   s = tmp;
  819.   s[0] = s[1] = '\0';        /* start with empty flag string */
  820.                 /* output system flags */
  821.   if (elt->recent) strcat (s," \\Recent");
  822.   if (elt->seen) strcat (s," \\Seen");
  823.   if (elt->deleted) strcat (s," \\Deleted");
  824.   if (elt->flagged) strcat (s," \\Flagged");
  825.   if (elt->answered) strcat (s," \\Answered");
  826.   if (u = elt->user_flags) do    /* any user flags? */
  827.     if ((TMPLEN - ((s += strlen (s)) - tmp)) >
  828.     (2 + strlen (t = stream->user_flags[find_rightmost_bit (&u)]))) {
  829.       *s++ = ' ';        /* space delimiter */
  830.       strcpy (s,t);        /* copy the user flag */
  831.     }
  832.   while (u);            /* until no more user flags */
  833.   printf ("FLAGS (%s)",tmp+1);    /* output results, skip first char of list */
  834. }
  835.  
  836.  
  837. /* Output flags if was unseen
  838.  * Accepts: message number
  839.  *        prior value of Seen flag
  840.  */
  841.  
  842. void changed_flags (i,f)
  843.     long i;
  844.     int f;
  845. {
  846.   if (!f) {            /* was unseen? */
  847.     putchar (' ');        /* yes, delimit with space */
  848.     fetch_flags (i,NIL);    /* output flags */
  849.   }
  850. }
  851.  
  852.  
  853. /* Fetch message internal date
  854.  * Accepts: message number
  855.  *        string argument
  856.  */
  857.  
  858. void fetch_internaldate (i,s)
  859.     long i;
  860.     char *s;
  861. {
  862.   char tmp[MAILTMPLEN];
  863.   printf ("INTERNALDATE \"%s\"",mail_date (tmp,mail_elt (stream,i)));
  864. }
  865.  
  866. /* Fetch complete RFC-822 format message
  867.  * Accepts: message number
  868.  *        string argument
  869.  */
  870.  
  871. void fetch_rfc822 (i,s)
  872.     long i;
  873.     char *s;
  874. {
  875.   int f = mail_elt (stream,i)->seen;
  876.   printf ("RFC822 {%d}\015\012%s",mail_elt (stream,i)->rfc822_size,
  877.       mail_fetchheader (stream,i));
  878.   fputs (mail_fetchtext (stream,i),stdout);
  879.   changed_flags (i,f);        /* output changed flags */
  880. }
  881.  
  882.  
  883. /* Fetch RFC-822 header
  884.  * Accepts: message number
  885.  *        string argument
  886.  */
  887.  
  888. void fetch_rfc822_header (i,s)
  889.     long i;
  890.     char *s;
  891. {
  892.   fputs ("RFC822.HEADER ",stdout);
  893.   pstring (mail_fetchheader (stream,i));
  894. }
  895.  
  896.  
  897. /* Fetch RFC-822 message length
  898.  * Accepts: message number
  899.  *        string argument
  900.  */
  901.  
  902. void fetch_rfc822_size (i,s)
  903.     long i;
  904.     char *s;
  905. {
  906.   printf ("RFC822.SIZE %d",mail_elt (stream,i)->rfc822_size);
  907. }
  908.  
  909.  
  910. /* Fetch RFC-822 text only
  911.  * Accepts: message number
  912.  *        string argument
  913.  */
  914.  
  915. void fetch_rfc822_text (i,s)
  916.     long i;
  917.     char *s;
  918. {
  919.   int f = mail_elt (stream,i)->seen;
  920.   fputs ("RFC822.TEXT ",stdout);
  921.   pstring (mail_fetchtext (stream,i));
  922.   changed_flags (i,f);        /* output changed flags */
  923. }
  924.  
  925. /* Print envelope
  926.  * Accepts: body
  927.  */
  928.  
  929. void penv (env)
  930.     ENVELOPE *env;
  931. {
  932.   if (env) {            /* only if there is an envelope */
  933.                 /* disgusting MacMS kludge */
  934.     if (mackludge) printf ("%d ",cstring (env->date) + cstring (env->subject) +
  935.                caddr (env->from) + caddr (env->sender) +
  936.                caddr (env->reply_to) + caddr (env->to) +
  937.                caddr (env->cc) + caddr (env->bcc) +
  938.                cstring (env->in_reply_to) +
  939.                cstring (env->message_id));
  940.     putchar ('(');        /* delimiter */
  941.     pstring (env->date);    /* output envelope fields */
  942.     putchar (' ');
  943.     pstring (env->subject);
  944.     putchar (' ');
  945.     paddr (env->from);
  946.     putchar (' ');
  947.     paddr (env->sender);
  948.     putchar (' ');
  949.     paddr (env->reply_to);
  950.     putchar (' ');
  951.     paddr (env->to);
  952.     putchar (' ');
  953.     paddr (env->cc);
  954.     putchar (' ');
  955.     paddr (env->bcc);
  956.     putchar (' ');
  957.     pstring (env->in_reply_to);
  958.     putchar (' ');
  959.     pstring (env->message_id);
  960.     putchar (')');        /* end of envelope */
  961.   }
  962.   else fputs ("NIL",stdout);    /* no envelope */
  963. }
  964.  
  965. /* Print body
  966.  * Accepts: body
  967.  */
  968.  
  969. void pbody (body)
  970.     BODY *body;
  971. {
  972.   if (body) {            /* only if there is a body */
  973.     PARAMETER *param;
  974.     PART *part;
  975.     putchar ('(');        /* delimiter */
  976.                 /* multipart type? */
  977.     if (body->type == TYPEMULTIPART) {
  978.       for (part = body->contents.part; part; part = part->next)
  979.     pbody (&(part->body));    /* print each part */
  980.       putchar (' ');        /* space delimiter */
  981.       pstring (body->subtype);    /* and finally the subtype */
  982.     }
  983.     else {            /* non-multipart body type */
  984.       pstring ((char *) body_types[body->type]);
  985.       putchar (' ');
  986.       pstring (body->subtype);
  987.       if (param = body->parameter) {
  988.     fputs (" (",stdout);
  989.     do {
  990.       pstring (param->attribute);
  991.       putchar (' ');
  992.       pstring (param->value);
  993.       if (param = param->next) putchar (' ');
  994.     } while (param);
  995.     fputs (") ",stdout);
  996.       }
  997.       else fputs (" NIL ",stdout);
  998.       pstring (body->id);
  999.       putchar (' ');
  1000.       pstring (body->description);
  1001.       putchar (' ');
  1002.       pstring ((char *) body_encodings[body->encoding]);
  1003.       printf (" %d",body->size.bytes);
  1004.       switch (body->type) {    /* extra stuff depends upon body type */
  1005.       case TYPEMESSAGE:
  1006.                 /* can't do this if not RFC822 */
  1007.     if (strcmp (body->subtype,"RFC822")) break;
  1008.     putchar (' ');
  1009.     penv (body->contents.msg.env);
  1010.     putchar (' ');
  1011.     pbody (body->contents.msg.body);
  1012.       case TYPETEXT:
  1013.     printf (" %d",body->size.lines);
  1014.     break;
  1015.       default:
  1016.     break;
  1017.       }
  1018.     }
  1019.     putchar (')');        /* end of body */
  1020.   }
  1021.   else fputs ("NIL",stdout);    /* no body */
  1022. }
  1023.  
  1024. /* Print string
  1025.  * Accepts: string
  1026.  */
  1027.  
  1028. void pstring (s)
  1029.     char *s;
  1030. {
  1031.   char c,*t;
  1032.   if (s) {            /* is there a string? */
  1033.                 /* must use literal string */
  1034.     if (strpbrk (s,"\012\015\"%{\\")) printf ("{%d}\015\012%s",strlen (s),s);
  1035.     else printf ("\"%s\"",s);    /* may use quoted string */
  1036.   }
  1037.   else fputs ("NIL",stdout);    /* empty string */
  1038. }
  1039.  
  1040.  
  1041. /* Print address list
  1042.  * Accepts: address list
  1043.  */
  1044.  
  1045. void paddr (a)
  1046.     ADDRESS *a;
  1047. {
  1048.   if (a) {            /* have anything in address? */
  1049.     putchar ('(');        /* open the address list */
  1050.     do {            /* for each address */
  1051.       putchar ('(');        /* open the address */
  1052.       pstring (a->personal);    /* personal name */
  1053.       putchar (' ');
  1054.       pstring (a->adl);        /* at-domain-list */
  1055.       putchar (' ');
  1056.       pstring (a->mailbox);    /* mailbox */
  1057.       putchar (' ');
  1058.       pstring (a->host);    /* domain name of mailbox's host */
  1059.       putchar (')');        /* terminate address */
  1060.     } while (a = a->next);    /* until end of address */
  1061.     putchar (')');        /* close address list */
  1062.   }
  1063.   else fputs ("NIL",stdout);    /* empty address */
  1064. }
  1065.  
  1066. /* Count string and space afterwards
  1067.  * Accepts: string
  1068.  * Returns: 1 plus length of string
  1069.  */
  1070.  
  1071. long cstring (s)
  1072.     char *s;
  1073. {
  1074.   char tmp[20];
  1075.   long i = s ? strlen (s) : 0;
  1076.   if (s) {            /* is there a string? */
  1077.                 /* must use literal string */
  1078.     if (strpbrk (s,"\012\015\"%{\\")) {
  1079.       sprintf (tmp,"{%d}\015\012",i);
  1080.       i += strlen (tmp);
  1081.     }
  1082.     else i += 2;        /* quoted string */
  1083.   }
  1084.   else i += 3;            /* NIL */
  1085.   return i + 1;            /* return string plus trailing space */
  1086. }
  1087.  
  1088.  
  1089. /* Count address list and space afterwards
  1090.  * Accepts: address list
  1091.  */
  1092.  
  1093. long caddr (a)
  1094.     ADDRESS *a;
  1095. {
  1096.   long i = 3;            /* open, close, and space */
  1097.                 /* count strings in address list */
  1098.   if (a) do i += 1 + cstring (a->personal) + cstring (a->adl) +
  1099.     cstring (a->mailbox) + cstring (a->host);
  1100.   while (a = a->next);        /* until end of address */
  1101.   else i = 4;            /* NIL plus space */
  1102.   return i;            /* return the count */
  1103. }
  1104.  
  1105. /* Co-routines from MAIL library */
  1106.  
  1107.  
  1108. /* Message matches a search
  1109.  * Accepts: IMAP2 stream
  1110.  *        message number
  1111.  */
  1112.  
  1113. void mm_searched (s,msgno)
  1114.     MAILSTREAM *s;
  1115.     long msgno;
  1116. {
  1117.                 /* nothing to do here */
  1118. }
  1119.  
  1120.  
  1121. /* Message exists (mailbox)
  1122.     i.e. there are that many messages in the mailbox;
  1123.  * Accepts: IMAP2 stream
  1124.  *        message number
  1125.  */
  1126.  
  1127. void mm_exists (s,number)
  1128.     MAILSTREAM *s;
  1129.     long number;
  1130. {
  1131.   if (s != tstream) {        /* note change in number of messages */
  1132.     printf ("* %d EXISTS\015\012",(nmsgs = number));
  1133.     recent = -1;        /* make sure fetch new recent count */
  1134.   }
  1135. }
  1136.  
  1137.  
  1138. /* Message expunged
  1139.  * Accepts: IMAP2 stream
  1140.  *        message number
  1141.  */
  1142.  
  1143. void mm_expunged (s,number)
  1144.     MAILSTREAM *s;
  1145.     long number;
  1146. {
  1147.   if (s != tstream) printf ("* %d EXPUNGE\015\012",number);
  1148. }
  1149.  
  1150.  
  1151. /* Mailbox found
  1152.  * Accepts: Mailbox name
  1153.  */
  1154.  
  1155. void mm_mailbox (string)
  1156.     char *string;
  1157. {
  1158.   printf ("* MAILBOX %s\015\012",string);
  1159. }
  1160.  
  1161.  
  1162. /* BBoard found
  1163.  * Accepts: BBoard name
  1164.  */
  1165.  
  1166. void mm_bboard (string)
  1167.     char *string;
  1168. {
  1169.   printf ("* BBOARD %s\015\012",string);
  1170. }
  1171.  
  1172. /* Notification event
  1173.  * Accepts: IMAP2 stream
  1174.  *        string to log
  1175.  *        error flag
  1176.  */
  1177.  
  1178. void mm_notify (s,string,errflg)
  1179.     MAILSTREAM *s;
  1180.     char *string;
  1181.     long errflg;
  1182. {
  1183.   if (!tstream || (s != tstream)) switch (errflg) {
  1184.   case NIL:            /* information message, set as OK response */
  1185.   case PARSE:            /* parse glitch, output unsolicited OK */
  1186.     printf ("* OK %s\015\012",string);
  1187.     break;
  1188.   case WARN:            /* warning, output unsolicited NO (kludge!) */
  1189.     printf ("* NO %s\015\012",string);
  1190.     break;
  1191.   case ERROR:            /* error that broke command */
  1192.   default:            /* default should never happen */
  1193.     printf ("* BAD %s\015\012",string);
  1194.     break;
  1195.   }
  1196. }
  1197.  
  1198. /* Log an event for the user to see
  1199.  * Accepts: string to log
  1200.  *        error flag
  1201.  */
  1202.  
  1203. void mm_log (string,errflg)
  1204.     char *string;
  1205.     long errflg;
  1206. {
  1207.   switch (errflg) {        /* action depends upon the error flag */
  1208.   case NIL:            /* information message, set as OK response */
  1209.     if (response == win) {    /* only if no other response yet */
  1210.       response = altwin;    /* switch to alternative win message */
  1211.       fs_give ((void **) &lsterr);
  1212.       lsterr = cpystr (string);    /* copy string for later use */
  1213.     }
  1214.     break;
  1215.   case PARSE:            /* parse glitch, output unsolicited OK */
  1216.     printf ("* OK [PARSE] %s\015\012",string);
  1217.     break;
  1218.   case WARN:            /* warning, output unsolicited NO (kludge!) */
  1219.     if (strcmp (string,"Mailbox is empty")) printf ("* NO %s\015\012",string);
  1220.     break;
  1221.   case ERROR:            /* error that broke command */
  1222.   default:            /* default should never happen */
  1223.     response = lose;        /* set fatality */
  1224.     fs_give ((void **) &lsterr);/* flush old error */
  1225.     lsterr = cpystr (string);    /* note last error */
  1226.     break;
  1227.   }
  1228. }
  1229.  
  1230.  
  1231. /* Log an event to debugging telemetry
  1232.  * Accepts: string to log
  1233.  */
  1234.  
  1235. void mm_dlog (string)
  1236.     char *string;
  1237. {
  1238.   mm_log (string,WARN);        /* shouldn't happen normally */
  1239. }
  1240.  
  1241. /* Get user name and password for this host
  1242.  * Accepts: host name
  1243.  *        where to return user name
  1244.  *        where to return password
  1245.  *        trial count
  1246.  */
  1247.  
  1248. void mm_login (host,username,password,trial)
  1249.     char *host;
  1250.     char *username;
  1251.     char *password;
  1252.     long trial;
  1253. {
  1254.   strcpy (username,user);    /* set user name */
  1255.   strcpy (password,pass);    /* and password */
  1256. }
  1257.  
  1258. /* About to enter critical code
  1259.  * Accepts: stream
  1260.  */
  1261.  
  1262. void mm_critical (s)
  1263.     MAILSTREAM *s;
  1264. {
  1265.   /* Not doing anything here for now */
  1266. }
  1267.  
  1268.  
  1269. /* About to exit critical code
  1270.  * Accepts: stream
  1271.  */
  1272.  
  1273. void mm_nocritical (s)
  1274.     MAILSTREAM *s;
  1275. {
  1276.   /* Not doing anything here for now */
  1277. }
  1278.  
  1279.  
  1280. /* Disk error found
  1281.  * Accepts: stream
  1282.  *        system error code
  1283.  *        flag indicating that mailbox may be clobbered
  1284.  * Returns: abort flag
  1285.  */
  1286.  
  1287. long mm_diskerror (s,errcode,serious)
  1288.     MAILSTREAM *s;
  1289.     long errcode;
  1290.     long serious;
  1291. {
  1292.   if (serious) {        /* try your damnest if clobberage likely */
  1293.     fputs ("* BAD Retrying to fix probable mailbox damage!\015\012",stdout);
  1294.     fflush (stdout);        /* dump output buffer */
  1295.                 /* make damn sure timeout disabled */
  1296.     timer.it_value.tv_sec = timer.it_value.tv_usec = 0;
  1297.     setitimer (ITIMER_REAL,&timer,NIL);
  1298.     sleep (60);            /* give it some time to clear up */
  1299.     return NIL;
  1300.   }
  1301.                 /* otherwise die before more damage is done */
  1302.   printf ("* BYE Aborting due to disk error %s\015\012",strerror (errcode));
  1303.   return T;
  1304. }
  1305.  
  1306.  
  1307. /* Log a fatal error event
  1308.  * Accepts: string to log
  1309.  */
  1310.  
  1311. void mm_fatal (string)
  1312.     char *string;
  1313. {
  1314.   mm_log (string,ERROR);    /* shouldn't happen normally */
  1315. }
  1316.